package cn.ztr.ymcc.controller;

import cn.ztr.ymcc.CourseDOC;
import cn.ztr.ymcc.mapper.HighlightResultMapper;
import cn.ztr.ymcc.query.CourerQuery;
import cn.ztr.ymcc.repository.SearchRepository;
import cn.ztr.ymcc.result.JsonResult;
import cn.ztr.ymcc.util.StrUtils;
import cn.ztr.ymcc.vo.AggrPageList;
import cn.ztr.ymcc.vo.AggrTermsBucket;
import cn.ztr.ymcc.vo.PageList;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.BucketOrder;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.annotation.Id;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.DefaultResultMapper;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/search")
public class SearchController {
    @Autowired
    private SearchRepository searchRepository;
    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;
    @Autowired
    private HighlightResultMapper highlightResultMapper;

    /**
     * 批量添加课程信息到es
     * @param courseDOCList
     * @return
     */
    @PostMapping("/saveBatch")
    public JsonResult saveBatch(@RequestBody List<CourseDOC> courseDOCList){
            searchRepository.saveAll(courseDOCList);
            return JsonResult.me();
    }

    /**
     * 批量删除课程信息从es
     * @param courseDOCList
     * @return
     */
    @PostMapping("/deleteBatch")
    public JsonResult deleteBatch(@RequestBody List<CourseDOC> courseDOCList){
            searchRepository.deleteAll(courseDOCList);
            return JsonResult.me();
    }

    /**
     * es课程搜索
     * @param courerQuery
     * @return
     */
    @PostMapping("/queryCourse")
    public JsonResult queryCourse(@RequestBody CourerQuery courerQuery){
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        //组装条件
        BoolQueryBuilder bool = QueryBuilders.boolQuery();
        if (StringUtils.hasText(courerQuery.getKeyword()))
            bool.filter().add(QueryBuilders.matchQuery("name",courerQuery.getKeyword()));
        if (StringUtils.hasText(courerQuery.getCourseTypeName()))
            bool.filter().add(QueryBuilders.termQuery("courseTypeName",courerQuery.getCourseTypeName()));
        if (courerQuery.getCourseTypeId()!=null)
            bool.filter().add(QueryBuilders.termQuery("courseTypeId",courerQuery.getCourseTypeId()));
        if (courerQuery.getGradeId()!=null)
            bool.filter().add(QueryBuilders.termQuery("gradeId",courerQuery.getGradeId()));
        if (StringUtils.hasText(courerQuery.getGradeName()))
            bool.filter().add(QueryBuilders.termQuery("gradeName",courerQuery.getGradeName()));
        if (courerQuery.getPriceMin()!=null)
            bool.filter().add(QueryBuilders.rangeQuery("price").gte(courerQuery.getPriceMin()));
        if (courerQuery.getPriceMax()!=null)
            bool.filter().add(QueryBuilders.rangeQuery("price").lte(courerQuery.getPriceMax()));
        queryBuilder.withQuery(bool);
        //添加分页
        queryBuilder.withPageable(PageRequest.of(courerQuery.getPageNo()-1,courerQuery.getPageSize()));
        //添加排序
        if (StringUtils.hasText(courerQuery.getOrderField())){
            SortBuilder sortBuilder = SortBuilders.fieldSort(courerQuery.getOrderField()).order(SortOrder.DESC);
            if("asc".equalsIgnoreCase(courerQuery.getOrderType())){
                sortBuilder.order(SortOrder.ASC);
            }
            queryBuilder.withSort(sortBuilder);
        }

        //4.执行搜索之前，添加关键字高亮的条件【高亮只能用在分词的字段上面】
        HighlightBuilder.Field  highlightField = new HighlightBuilder.Field("name")
                .preTags("<span style='color:red'>")
                .postTags("</span>");
        queryBuilder.withHighlightFields(highlightField);

        //5.添加聚合统计的条件
        queryBuilder.addAggregation(AggregationBuilders
                .terms("courseTypeName")       //聚合统计取一个别名  要通过此别名来提取统计结果
                .field("courseTypeName")	            //分组聚合的字段
                .order(BucketOrder.count(false)).size(20));
        queryBuilder.addAggregation(AggregationBuilders
                .terms("gradeName")       //聚合统计取一个别名  要通过此别名来提取统计结果
                .field("gradeName")	            //分组聚合的字段
                .order(BucketOrder.count(false)).size(20));

        AggregatedPage<CourseDOC> search = elasticsearchRestTemplate.queryForPage(queryBuilder.build(), CourseDOC.class, highlightResultMapper);
        //Page<CourseDOC> search = searchRepository.search(queryBuilder.build());
        //获取当前页数据
        List<CourseDOC> content = search.getContent();
        //获取总条数
        Long totalElements = search.getTotalElements();
        //7.获取聚合统计的结果
        Map<String, List<AggrTermsBucket>> aggrResult = this.getAggrResult(search);
        //封装我们自己的PageList
        AggrPageList<CourseDOC> pageList = new AggrPageList<>(search.getTotalElements(), content, aggrResult);
        //封装数据进行返回
        return JsonResult.me(pageList);

    }

    private Map<String, List<AggrTermsBucket>> getAggrResult(AggregatedPage<CourseDOC> page) {
        //用来封装聚合结果
        Map<String,List<AggrTermsBucket>> aggrMap = new HashMap<>();

        Aggregations aggregations = page.getAggregations();
        //遍历聚合结果：键是在我们执行搜索之前 自定义的聚合条件别名
        Map<String, Aggregation> stringAggregationMap = aggregations.asMap();


        Set<Map.Entry<String, Aggregation>> entries = stringAggregationMap.entrySet();
        entries.forEach(e->{
            //集合的名字
            String key = e.getKey();
            //集合的值的处理
            ParsedStringTerms value = (ParsedStringTerms)e.getValue();
            List<? extends Terms.Bucket> buckets = value.getBuckets();
            //使用lomda 把  List<StringTerms.Bucket>  转成  List<AggrTermsBucket>
            List<AggrTermsBucket> aggrTermsVos = buckets.stream().map(bucket -> {
                return new AggrTermsBucket(bucket.getKeyAsString(), bucket.getDocCount());
            }).collect(Collectors.toList());
            aggrMap.put(key, aggrTermsVos);
        });
        return aggrMap;
    }

    /**
     * 根据课程id查询信息
     * @param courseIds
     * @return
     */
    @PostMapping("/info")
    public JsonResult info(@RequestBody List<Long> courseIds){
        List<CourseDOC> courseList = (List<CourseDOC>) searchRepository.findAllById(courseIds);
        return JsonResult.me(courseList);
    }

}
